#ifndef __SERIALIOI2CLIB_H__
#define __SERIALIOI2CLIB_H__
#pragma once
#include <Windows.h>
#include "SerialIoI2cRegs.h"
#define EFI_SUCCESS	0
#define EFI_INVALID_PARAMETER	(-2)
#define EFI_DEVICE_ERROR	(-7)
#define EFI_TIMEOUT	(-18)
#define EFI_BAD_BUFFER_SIZE	(-4)
#define EFI_NOT_READY	(-8)
#define DEBUG_WARN      0x00000002		// Warnings
#define DEBUG_ERROR		0x80000000		// Error
typedef UINT64 UINTN;
typedef unsigned char BOOLEAN;
typedef UINTN RETURN_STATUS;
typedef RETURN_STATUS EFI_STATUS;
#define STATIC	static
#ifdef DEBUG
#undef DEBUG
#endif
#define DEBUG(Expression) LogEvent Expression

#define DEBUG_ERROR  0x80000000     // Error
#define LShiftU64(Operand, Count) (UINTN)((UINTN)Operand << Count)

//  PCH LP & H Serial IO I2C #0 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C0          21
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C0        0
//  PCH LP & H Serial IO I2C #1 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C1          21
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C1        1
//  PCH LP & H Serial IO I2C #2 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C2          21
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C2        2
//  PCH LP & H Serial IO I2C #3 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C3          21
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C3        3
//  PCH LP & H Serial IO I2C #4 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C4          25
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C4        0
//  PCH LP & H Serial IO I2C #5 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C5          25
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C5        1
//  PCH LP & H Serial IO I2C #6 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C6          16
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C6        0
//  PCH LP & H Serial IO I2C #7 Controller
#define PCI_DEVICE_NUMBER_PCH_SERIAL_IO_I2C7          16
#define PCI_FUNCTION_NUMBER_PCH_SERIAL_IO_I2C7        1
#define PCIEX_BASE_ADDRESS							  0xC0000000

#define MmioAnd32(Address,data) MmioWrite32((Address),(MmioRead32(Address) & (data)))
#define MmioOr32(Address,data) MmioWrite32((Address),(MmioRead32(Address) | (data)))
#define MmioAnd8(Address,data) MmioWrite8((Address),(MmioRead8(Address) & (data)))
#define MmioOr8(Address,data) MmioWrite8((Address),(MmioRead8(Address) | (data)))
#define MmioAndThenOr8(Address,And,Or) MmioWrite8((Address),((MmioRead8(Address) & (And)) | (Or)))
#define PCIEX_LIB_ADDRESS(Bus, Device, Function, Register)   \
  (((Register) & 0xfff) | (((Function) & 0x07) << 12) | (((Device) & 0x1f) << 15) | (((Bus) & 0xff) << 20))

#define PCIEX_CFG_ADDRESS(Bus, Device, Function)  PCIEX_BASE_ADDRESS | PCIEX_LIB_ADDRESS(Bus,Device,Function,0)

/**
Speed definitions for I2C Control Register
**/
typedef enum {
	I2cSpeedStandard = 0x1,      //[1KHz, 400KHz)
	I2cSpeedFast = 0x2,      //[400KHz, 3400KHZ)
	I2cSpeedHigh = 0x3,      //[3400, inf)
} SERIAL_IO_I2C_SPEED_SETTING;

/**
Internal Timeout definitions for Serial Io I2C
**/
typedef enum {
	I2cEnableTimeout = 50000,
	I2cDisableTimeout = 50000,
	I2cReceiveTimeout = 250000, // 250 ms -Applies to single byte timing
								// The slowest Freq for 50 cycles to complete should be around 1ms, might consider lowering it
	I2cWaitTxEmptyTimeout = 50000
} SERIAL_IO_I2C_TIMERS;

typedef enum {
	I2cBlockCommandWait = 0,
	I2cBlockCommandRead = 1,
	I2cBlockCommandWrite = 2
} SERIAL_IO_I2C_COMMAND;

typedef struct {
	UINT8  CmdId;
	UINT8  *CommandPointer;
	UINT32 CommandData;
} SERIALIO_I2C_BLOCK_COMMAND;

typedef struct {
	UINT16  SclHcnt;
	UINT16  SclLcnt;
	UINT16  SdaRxHold;
	UINT16  SdaTxHold;
} SERIAL_IO_I2C_TIMING;

void LogEvent(UINT32 debugLevel, LPCSTR pFormat, ...);

UINTN MicroSecondDelay(UINTN delay);

UINT8
SerialIoI2cDevNumber(
	IN UINT8       I2cNumber
	);

UINT8
SerialIoI2cFuncNumber(
	IN UINT8       I2cNumber
	);

/**
Checks I2C ic_en status bit

@param[in]  MmioBase   MMIO Base Address of specified I2C controller

@retval   TRUE    I2C is in enabled state
@retval   FALSE   I2C is inactive
**/
BOOLEAN
STATIC
SerialIoI2cIcEnableStatus(
	IN UINT64  MmioBase
	);

/**
Enable I2C device when in PCI only if it has assigned Memory resources

- Enable Memory space decoding
- Place device in D0
- Get it out of reset

Also checks for SCL/SDA line states.

@param[in]       PciCfgBase    Pci Config Base Address of specified I2C controller
@param[in][out]  BaseAddress   MMIO Base Address of specified I2C controller

@retval   TRUE    I2C successfully enabled
@retval   FALSE   I2C does not have memory assigned or SDA/SCL are not high
**/
BOOLEAN
SerialIoI2cPciEnable(
	IN UINTN           PciCfgBase,
	IN OUT UINT64      *BaseAddress
	);

/**
Disable I2C device when in PCI only if it has assigned Memory resources

- Disables DW_apb_i2c (TX and RX FIFOs are held in an erased state)
- Place it in reset state
- Place device in D3
- Disable Memory space decoding

@param[in]       PciCfgBase    Pci Config Base Address of specified I2C controller

@retval   TRUE    I2C successfully enabled
@retval   FALSE   I2C does not have memory assigned
**/
VOID
SerialIoI2cPciDisable(
	IN UINTN           PciCfgBase
	);

/**
Enables DW_apb_i2c and checks for ic_en status

@param[in]  MmioBase   MMIO Base Address of specified I2C controller

@retval   EFI_SUCCESS    I2C successfully enabled
@retval   EFI_TIMEOUT    I2C did not enable in given time refer to I2cEnableTimeout
**/
EFI_STATUS
SerialIoI2cEnableController(
	IN UINTN   MmioBase
	);

/**
Disables DW_apb_i2c (TX and RX FIFOs are held in an erased state) and checks for ic_en status

@param[in]  MmioBase   MMIO Base Address of specified I2C controller

@retval   EFI_SUCCESS    I2C successfully disabled
@retval   EFI_TIMEOUT    I2C did not enable in given time refer to I2cDisableTimeout
**/
EFI_STATUS
SerialIoI2cDisableController(
	IN UINTN   MmioBase
	);

/**
Clear software clearable interrupts and TX_ABRT interrupt of IC_RAW_INTR_STAT and ABRT_SOUCE registers
This clear is done by reading the I2C_MEM_CLR_INTR and I2C_MEM_CLR_TX_ABRT registers

@param[in]   MmioBase   MMIO Base Address of specified I2C controller
**/
VOID
SerialIoI2cClearErrors(
	IN UINTN  MmioBase
	);

/**
Set the SDA Hold and speed specific SCL high count and SCL low count registers

@param[in]       MmioBase       MMIO Base Address of specified I2C controller
@param[in][out]  Frequency      Pointer to bus clock frequency (in kHz)
@param[in]       TimingConfig   Optional: Setup variable struct containing override values for SCL LCNT/HCNT
User can override version specific timer values in PCH Setup or specify NULL
to use default config
**/
VOID
SerialIoI2cSetFrequency(
	IN     UINTN                 MmioBase,
	IN OUT UINTN                 *Frequency,
	IN     SERIAL_IO_I2C_TIMING  *TimingConfig   OPTIONAL
	);

/**
Write the given slave address to the I2C Target Address Register

@param[in]   MmioBase       MMIO Base Address of specified I2C controller
@param[in]   SlaveAddress   Address of the end point device
**/
VOID
SerialIoI2cSetSlaveAddress(
	IN UINTN   MmioBase,
	IN UINT32  SlaveAddress
	);

BOOL SerialIoI2cReset(UINT8* I2cNumber);
/**
Configure the I2C Control Register and start the controller

@param[in]   MmioBase     MMIO Base Address of specified I2C controller
@param[in]   Frequency    Pointer to bus clock frequency (in kHz)

@retval   EFI_TIMEOUT   I2C enable confirmation timed out
@retval   EFI_SUCCESS   I2C successfully enabled
**/
VOID
SerialIoI2cStartOperation(
	IN UINTN   MmioBase,
	IN UINTN   *Frequency
	);

/**
Handle the writing operations of the I2C controller

@param[in][out]   WriteLength  Pointer to write length
@param[in][out]   WriteBuffer  Double pointer to data in write buffer
@param[in]        MmioBase     MMIO Base Address of specified I2C controller
@param[in]        Stop         Issues STOP on the last byte
@param[in]        Restart      Indication if restart is needed

@retval   TRUE       Data added to TX FIFO
FALSE      TX FIFO is full or at its limit - Write did not complete
**/
BOOLEAN
SerialIoI2cPrivateWrite(
	IN OUT UINT32  *WriteLength,
	IN OUT UINT8   **WriteBuffer,
	IN     UINTN   MmioBase,
	IN     BOOLEAN Stop,
	IN     BOOLEAN Restart
	);

/**
Handle the reading operations of the I2C controller

@param[in][out]   ReadLength   Pointer to read length
@param[in][out]   ReadBuffer   Double pointer to data in read buffer
@param[in]        MmioBase     MMIO Base Address of specified I2C controller
@param[in]        Restart      Indication if restart is needed
**/
EFI_STATUS
SerialIoI2cPrivateRead(
	IN OUT UINT32  *ReadLength,
	IN OUT UINT8   **ReadBuffer,
	IN     UINTN   MmioBase,
	IN     BOOLEAN Restart
	);

/**
Set up the I2C Controller for reading and/or writing

Before any transaction:
1. Verify I2C Controller is active
2. Disable I2C Controller
3. Clear any errors / interrupts
4. Set I2C frequency values
5. Write slave address to I2C target address register
6. Initiate I2C Controller

@param[in]   MmioBase        MMIO Base Address of specified I2C controller
@param[in]   Slave Address  -Address of the end point device
@param[in]   *Frequency      Pointer to bus clock frequency (in kHz)
@param[in]   *TimingConfig   Pointer to optional struct passed down to SerialIoI2cSetFrequency ()

@retval   EFI_TIMEOUT        Timeout occurred in I2C disable/enable
@retval   EFI_DEVICE_ERROR   The verification failed
@retval   EFI_SUCCESS        I2C W/R successfully initialized

**/
EFI_STATUS
SerialIoI2cInitializeWriteRead(
	IN UINTN                 MmioBase,
	IN UINT32                SlaveAddress,
	IN UINTN                 *Frequency,
	IN SERIAL_IO_I2C_TIMING  *TimingConfig  OPTIONAL
	);

/**
Get the I2C SDA Hold value for the given frequency

@param[in]       MmioBase    MMIO Base Address of specified I2C controller
@param[in][out]  SdaHold     Pointer to register union for R_I2C_MEM_SDA_HOLD
@param[in]       Frequency   Pointer to bus clock frequency (in kHz)
**/
VOID
SerialIoI2cGetSdaHold(
	IN      UINTN                       MmioBase,
	IN OUT  SERIAL_IO_I2C_MEM_SDA_HOLD  *SdaHold,
	IN      UINTN                       *Frequency
	);

/**
Get Standard Speed I2C Clock SCL High/Low Count Register value
Applies to frequency in range [100kHz, 400kHz)

@param[in]        MmioBase    MMIO Base Address of specified I2C controller
@param[in][out]   SsSclHcnt   Pointer to register union for R_I2C_MEM_SS_SCL_HCNT
@param[in][out]   SsSclLcnt   Pointer to register union for R_I2C_MEM_SS_SCL_LCNT
@param[in]        Frequency   Pointer to bus clock frequency (in kHz)
**/
VOID
SerialIoI2cGetSsScl(
	IN      UINTN                           MmioBase,
	IN OUT  SERIAL_IO_I2C_MEM_SS_SCL_HCNT  *SsSclHcnt,
	IN OUT  SERIAL_IO_I2C_MEM_SS_SCL_LCNT  *SsSclLcnt,
	IN      UINTN                          *Frequency
	);

/**
Get Fast Speed I2C Clock SCL High/Low Count Register value
Applies to frequency in range [400kHz, 3.4MHz)

@param[in]        MmioBase    MMIO Base Address of specified I2C controller
@param[in][out]   FsSclHcnt   Pointer to register union for R_I2C_MEM_FS_SCL_HCNT
@param[in][out]   FsSclLcnt   Pointer to register union for R_I2C_MEM_FS_SCL_LCNT
@param[in]        Frequency   Pointer to bus clock frequency (in kHz)
**/
VOID
SerialIoI2cGetFsScl(
	IN      UINTN                           MmioBase,
	IN OUT  SERIAL_IO_I2C_MEM_FS_SCL_HCNT  *FsSclHcnt,
	IN OUT  SERIAL_IO_I2C_MEM_FS_SCL_LCNT  *FsSclLcnt,
	IN      UINTN                          *Frequency
	);

/**
Get High Speed I2C Clock SCL High/Low Count Register value
Applies to frequency in range [3.4MHz, inf)

@param[in]        MmioBase    MMIO Base Address of specified I2C controller
@param[in][out]   HsSclHcnt   Pointer to register union for R_I2C_MEM_HS_SCL_HCNT
@param[in][out]   HsSclLcnt   Pointer to register union for R_I2C_MEM_HS_SCL_LCNT
@param[in]        Frequency   Pointer to bus clock frequency (in kHz)
**/
VOID
SerialIoI2cGetHsScl(
	IN      UINTN                           MmioBase,
	IN OUT  SERIAL_IO_I2C_MEM_HS_SCL_HCNT  *HsSclHcnt,
	IN OUT  SERIAL_IO_I2C_MEM_HS_SCL_LCNT  *HsSclLcnt,
	IN      UINTN                          *Frequency
	);

/**
Checks if transmission was aborted

@param[in]  MmioBase    MMIO Base Address of specified I2C controller

@retval     TRUE        Tx Abort occured
@retval     FALSE       No Tx Abort errors
**/
BOOLEAN
SerialIoI2cIsRTxAbrt(
	UINTN   MmioBase
	);

/**
Checks if Transmit FIFO is empty

@param[in]  MmioBase    MMIO Base Address of specified I2C controller

@retval     TRUE        Tx FIFO is empty
@retval     FALSE       Tx FIFO not empty
**/
BOOLEAN
SerialIoI2cIsTxFifoEmpty(
	UINTN   MmioBase
	);

/**
Dumps all Tx Abort Source register fields

@param[in]  MmioBase    MMIO Base Address of specified I2C controller
**/
VOID
SerialIoI2cDumpTxAbort(
	UINTN   MmioBase
	);

/**
Waits until Tx Fifo is empty and I2C no longer Active

@param[in]  MmioBase    MMIO Base Address of specified I2C controller

@retval   EFI_TIMEOUT   TX FIFO did not clear or I2C was still active
@retval   EFI_SUCCESS   TX FIFO is empty and I2C is idle
**/
EFI_STATUS
SerialIoI2cWaitTxFifoEmptyIcNotActive(
	UINTN   MmioBase
	);

/**
Execute an I2C write

Addressing mode will defined based upon SlaveAddres and mask:
SlaveAdress & 0x80000000 - if true will result in 10bit addressing
Refer to: I2C_ADDRESSING_10_BIT

@param[in]   PciCfgBase     Pci Config Base Address of specified I2C controller
@Note: If I2C Number is provided this parameter is ignored and can be left as 0
@param[in]   I2cNumber      Optional: I2C controller applicable on bus 0 only
@param[in]   SlaveAddress   Address of the end point device
@param[in]   WriteLength    Write length of the current transaction
@param[in]   WriteBuffer    Pointer to data in write buffer
@param[in]   TimeBudget     Time allotted to verify if I2C is enabled
@param[in]   Frequency      Bus clock frequency (in kHz)
@param[in]   TimingConfig   Optional: Pointer to struct passed down to override SDA Hold and SCL LCNT/HCNT settings
@param[in]   Stop           Issues STOP on the last byte
@param[in]   Restart        Indication if restart is needed, if TRUE will Initialize I2C controller.
@Note: Restart is required in first transaction.

@retval   EFI_INVALID_PARAMETER   Invalid length / buffer parameters were passed into this function
@retval   EFI_TIMEOUT             Timeout occurred in I2C disable/enable or wrong ENST value
@retval   EFI_DEVICE_ERROR        The verification failed
@retval   EFI_SUCCESS             Write was successful
**/
EFI_STATUS
SerialIoI2cWrite(
	IN UINTN                 PciCfgBase,
	IN UINT8                 *I2cNumber  OPTIONAL,
	IN UINT32                SlaveAddress,
	IN UINT32                WriteLength,
	IN UINT8                 *WriteBuffer,
	IN UINT64                TimeBudget,
	IN UINTN                 Frequency,
	IN SERIAL_IO_I2C_TIMING  *TimingConfig  OPTIONAL,
	IN BOOLEAN               Stop,
	IN BOOLEAN               Restart
	);

/**
Execute an I2C read command

Addressing mode will defined based upon SlaveAddress and mask:
SlaveAdress & 0x80000000 - if true will result in 10bit addressing
Refer to: I2C_ADDRESSING_10_BIT

@param[in]   PciCfgBase     Pci Config Base Address of specified I2C controller
@Note: If I2C Number is provided this parameter is ignored and can be left as 0
@param[in]   I2cNumber      Optional: I2C controller applicable on bus 0 only
@param[in]   SlaveAddress  -Address of the end point device
@param[in]   ReadLength     Read length of the current transaction
@param[in]   ReadBuffer     Pointer to data in read buffer
@param[in]   TimeBudget     Time allotted to verify if I2C is enabled
@param[in]   Frequency      Bus clock frequency (in kHz)
@param[in]   TimingConfig   Optional: Pointer to struct passed down to override SDA Hold and SCL LCNT/HCNT settings
@param[in]   Restart        Indication if restart is needed, if TRUE will Initialize I2C controller.
@Note: Restart is required in first transaction.

@retval   EFI_INVALID_PARAMETER   Invalid length / buffer parameters were passed into this function
@retval   EFI_TIMEOUT             Timeout occurred in I2C disable/enable or wrong ENST value
@retval   EFI_DEVICE_ERROR        The verification failed
@retval   EFI_SUCCESS             Read was successful
**/
EFI_STATUS
SerialIoI2cRead(
	IN UINTN                 PciCfgBase,
	IN UINT8                 *I2cNumber  OPTIONAL,
	IN UINT32                SlaveAddress,
	IN UINT32                ReadLength,
	IN UINT8                 *ReadBuffer,
	IN UINT64                TimeBudget,
	IN UINTN                 Frequency,
	IN SERIAL_IO_I2C_TIMING  *TimingConfig  OPTIONAL,
	IN BOOLEAN               Restart
	);

/**
Executes a block of I2C read/write and wait commands. This is intended to enable or configure devices that require multiple data transactions through a I2C bus.
The command structure consists of entries with a command id, input/output data pointer and data count.

The following is an example of a command block:
SERIALIO_I2C_BLOCK_COMMAND SampleCommandBlock[] = {
{ I2cBlockCommandWrite, &gWriteDataBuffer[0], sizeof (gWriteDataBuffer)},
{ I2cBlockCommandRead, &gReadDataBuffer[0], 1000},
{ I2cBlockCommandWait, NULL, 1000},
{ I2cBlockCommandWrite, &gWriteDataBuffer[100], 100}
};

Status = SerialIoI2cBlockWriteRead (
PciCfgBase,
&I2cNumber,
0x10,
SampleCommandBlock,
sizeof(SampleCommandBlock)/sizeof(SERIALIO_I2C_BLOCK_COMMAND),
Frequency,
WAIT_1_SECOND,
TRUE,
&TimingConfig
);

@param[in]   PciCfgBase     Pci Config Base Address of specified I2C controller
@Note: If I2C Number is provided this parameter is ignored and can be left as 0
@param[in]   I2cNumber                    Optional: The serial controller to be used
@param[in]   SlaveAddress                 The address for the device
@param[in]   I2cCommandBlock              Pointer to SERIALIO_I2C_BLOCK_COMMAND structure with all commands and data
@param[in]   NumofBlocks                  Total amount of the command block entries - sizeof(SampleCommandBlock)/sizeof(SERIALIO_I2C_BLOCK_COMMAND)
@param[in]   Frequency                    Bus clock frequency (in kHz)
@param[in]   MaxTransactionTimeBudget     Maximum Time budget for a single transaction
@param[in]   DisableI2cAfterTransaction   If TRUE, I2c is disabled after transaction is finished.
@param[in]   TimingConfig                 Optional: Pointer to struct passed down to override SDA Hold and SCL LCNT/HCNT settings

@retval   EFI_INVALID_PARAMETER   Signals that a command or data count was invalid
@retval   EFI_BUFFER_TOO_SMALL    The last command could not be executed since the buffer ended before reading all parameters
@retval   EFI_SUCCESS             The command were executed without incident
**/
EFI_STATUS
SerialIoI2cBlockWriteRead(
	IN UINTN                       PciCfgBase,
	IN UINT8                       *I2cNumber  OPTIONAL,
	IN UINT32                      SlaveAddress,
	IN SERIALIO_I2C_BLOCK_COMMAND  *I2cCommandBlock,
	IN UINT32                      BlockSize,
	IN UINTN                       Frequency,
	IN UINT64                      MaxTransactionTimeBudget,
	IN BOOLEAN                     DisableI2cAfterTransaction,
	IN SERIAL_IO_I2C_TIMING        *TimingConfig  OPTIONAL
	);

#endif//__SERIALIOI2CLIB_H__
